package org.spamforces.dao.impl;

import com.mysql.jdbc.Connection;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.log4j.Logger;
import org.jacuzzi.core.GenericDaoImpl;
import org.jacuzzi.core.Row;
import org.jacuzzi.core.TypeOracle;
import org.spamforces.configuration.ApplicationConfiguration;
import org.spamforces.database.ApplicationDataSourceFactory;
import org.spamforces.exception.ApplicationException;
import org.spamforces.model.ApplicationEntity;

import java.util.*;

/**
 * Base class for many jacuzzi implementations of application's DAOs.
 *
 * @author Dmitry Levshunov
 */
public class ApplicationDaoImpl<T extends ApplicationEntity> extends GenericDaoImpl<T, Long> {
    private static final Logger logger = Logger.getLogger(ApplicationDaoImpl.class);
    private final Random random = new Random();

    private volatile long lastFindNowCall;
    private volatile Date lastFindNowResult;

    protected ApplicationDaoImpl() {
        super(ApplicationDataSourceFactory.getInstance());
    }

    protected long getRandomLong() {
        return random.nextLong()
                + 31 * System.nanoTime()
                + 1009 * Runtime.getRuntime().freeMemory()
                + 10017 * Runtime.getRuntime().totalMemory();
    }

    protected String getRandomToken() {
        return DigestUtils.sha1Hex(Long.toString(getRandomLong()) + getRandomLong());
    }

    @SuppressWarnings({"UnnecessaryBoxing"})
    public T find(long id) {
        return find(Long.valueOf(id));
    }

    @Override
    public synchronized Date findNow() {
        long now = System.currentTimeMillis();
        if (Math.abs(now - lastFindNowCall) < 250 && lastFindNowResult != null) {
            return lastFindNowResult;
        }

        lastFindNowCall = now;
        return lastFindNowResult = getJacuzzi().findDate("SELECT NOW()");
    }

    @Override
    protected void beginTransaction() {
        getJacuzzi().beginTransaction(Connection.TRANSACTION_READ_COMMITTED);
    }

    @Override
    protected void commit() {
        super.commit();
    }

    @Override
    public void insert(T object) {
        if (object.isPersistent()) {
            throw new ApplicationException("Object is already persistent [object=" + object + "].");
        }
        super.insert(object);
    }

    @Override
    protected void rollback() {
        super.rollback();
    }

    protected <O extends ApplicationEntity> List<O> convertFromRows(TypeOracle<O> typeOracle, List<Row> rows) {
        List<O> result = new ArrayList<>(rows.size());
        for (Row row : rows) {
            result.add(typeOracle.convertFromRow(row));
        }
        return result;
    }

    static {
        TimeZone.setDefault(ApplicationConfiguration.getTimeZone());
    }
}
